/** 
 * @file XmlParser.c
 * @Synopsis  xml's parser by Mini-XML
 * @author JzeTech 
 * @version 0.1 beta
 * @date 2009-08-27
 */

#include "XmlParser.h"

/* --------------------------------------------------------------------------*/
/** 
 * @Synopsis  create a xml file
 * 
 * @Param FILE *fp
 * 
 * @Returns mxml_node_t* 
 */
/* ----------------------------------------------------------------------------*/
mxml_node_t* CreateXMLFile(FILE *fp)
{
	mxml_node_t *xml;
	mxml_node_t *TermRegister;
	mxml_node_t *VideoPort;
	mxml_node_t *Port;
	mxml_node_t *AudioPort;
	mxml_node_t *MsgPort;
	mxml_node_t *UpdatePort;
	mxml_node_t *WebPort;
	mxml_node_t *Protocol;
	mxml_node_t *Version;
	mxml_node_t *Support;

	xml = mxmlNewXML("1.0");

	TermRegister = mxmlNewElement(xml, "TermRegister");
	mxmlElementSetAttrf(TermRegister, "DeviceID", "%d", 1111);
	mxmlElementSetAttrf(TermRegister, "DeviceIP", "%s", "192.168.140.55");
	mxmlElementSetAttrf(TermRegister, "DeviceLinkType", "%c", '0');
	mxmlElementSetAttrf(TermRegister, "DeviceMaxCount", "%d", 10);

	VideoPort = mxmlNewElement(TermRegister, "VideoPort");
	mxmlNewInteger(VideoPort, 3888);

	Port = mxmlNewElement(TermRegister, "Port");
	mxmlNewInteger(Port, 6888);

	AudioPort = mxmlNewElement(TermRegister, "AudioPort");
	mxmlNewInteger(AudioPort, 5888);

	MsgPort = mxmlNewElement(TermRegister, "MsgPort");
	mxmlNewInteger(MsgPort, 4888);

	UpdatePort = mxmlNewElement(TermRegister, "UpdatePort");
	mxmlNewInteger(UpdatePort, 8888);

	WebPort = mxmlNewElement(TermRegister, "WebPort");
	mxmlNewInteger(WebPort, 81);

	Protocol = mxmlNewElement(TermRegister, "Protocol");
	mxmlNewText(Protocol, 0, "JiuZhou_H264_V1.0");

	Version = mxmlNewElement(TermRegister, "Version");
	mxmlNewInteger(Version, 2);

	Support = mxmlNewElement(TermRegister, "Support");

	mxmlElementSetAttrf(Support, "NAT", "%s", "true");
	mxmlElementSetAttrf(Support, "Scheme", "%s", "true");
	mxmlElementSetAttrf(Support, "PtzLockRet", "%s", "true");
	mxmlElementSetAttrf(Support, "Video2", "%s", "false");

	mxmlSaveFile(xml, fp, MXML_NO_CALLBACK);

	return xml;
}


/* --------------------------------------------------------------------------*/
/** 
 * @Synopsis  
 */
/* ----------------------------------------------------------------------------*/
//Load a xml file and Parse it
mxml_node_t* ParseXMLFile(FILE *fp)
{
	mxml_node_t *treeRoot = NULL;

	treeRoot = mxmlLoadFile(treeRoot, fp, MXML_REAL_CALLBACK);
	//ele = treeRoot;
	//RecursionNodes(ele,treeRoot);

	return treeRoot;
}


/* --------------------------------------------------------------------------*/
/** 
 * @Synopsis  get node's value
 * 
 * @Param mxml_node_t* node
 * @Param mxml_node_t*treeRoot
 */
/* ----------------------------------------------------------------------------*/
//Parse data structure like "<VideoPort>3888</VideoPort>"
void mxmlGetNodeValue(mxml_node_t *node, mxml_node_t *treeRoot)
{
	
	char *strName;
	mxml_node_t *eleChild = node;
	eleChild = mxmlWalkNext(eleChild, treeRoot, MXML_DESCEND_FIRST);
	if(eleChild == NULL || eleChild->child == NULL)
		return;
	
	if(mxmlGetNodeAttrsValue(eleChild) == true)
		return;
	strName = eleChild->value.element.name;
	printf("%s:%s\n",strName,eleChild->child->value.text.string);
	while(1)
	{
		eleChild = mxmlWalkNext(eleChild, treeRoot, MXML_NO_DESCEND);
		if(eleChild == NULL)
			return;
		if(mxmlGetNodeAttrsValue(eleChild) == true)
			return;
		strName = eleChild->value.element.name;
		printf("%s:%s\n",strName,eleChild->child->value.text.string);
		
		if(eleChild->child == NULL)
			return;
	}
	
	return;
}


/* --------------------------------------------------------------------------*/
/** 
 * @Synopsis get node's attrs 
 * 
 * @Param mxml_node_t* node
 * 
 * @Returns   int
 */
/* ----------------------------------------------------------------------------*/
//Parse data structure like "<Support NAT="true" Scheme="true" PtzLockRet="true" Video2="false" />"
int mxmlGetNodeAttrsValue(mxml_node_t *node)
{
	int num = node->value.element.num_attrs;
	if(num <= 0)
		return false;
	int i = 0;
	for(; i < num; i++)
		printf("%s:%s\n",node->value.element.attrs[i].name,node->value.element.attrs[i].value);
	return true;		
}

/** 
 *   @brief 
 *   
 *   @param node
 */
void* GetNodeAttrsValue(mxml_node_t *node, const char* name, int num)
{
	//int num = node->value.element.num_attrs;
	if(num <= 0)
	return NULL;
	int i = 0;
	for(; i < num; i++)
		if(strcmp(node->value.element.attrs[i].name, name) == 0)
		        return node->value.element.attrs[i].value;
       return NULL;
}


/** 
 * @brief 
 * 
 * @param node
 * 
 * @return 
 */
int GetNodeAttrsNum(mxml_node_t* node)
{
	return node->value.element.num_attrs;
}

/* --------------------------------------------------------------------------*/
/** 
 * @Synopsis  
 * 
 * @Param mxml_node_t* node
 * @Param mxml_node_t* root
 */
/* ----------------------------------------------------------------------------*/
bool RecursionNodes(mxml_node_t *node, mxml_node_t *root)
{

	mxml_node_t *eleChild = node;
	mxml_node_t *treeRoot = root;
	char* strName = NULL;

	//special case:<Message CurrentDateTime="081809152009.17"/>
	if(eleChild == treeRoot)
	{
		eleChild = mxmlWalkNext(eleChild, treeRoot, MXML_DESCEND_FIRST);
		if(eleChild == NULL)
		{
			if(mxmlGetNodeAttrsValue(treeRoot) == true)
				return true;
			return false;
		}
	}
	else{
		eleChild = mxmlWalkNext(eleChild, treeRoot, MXML_DESCEND_FIRST);
		if(eleChild == NULL)
		{
			return false;
		}
		if(eleChild->child == NULL && eleChild->value.element.attrs == NULL){
			//sometimes the useful element in last_child,not in child
			if(eleChild->next == NULL)
				return false;
			else
				eleChild = eleChild->next;  
		}
	}
	RecursionNodes(eleChild,root);

	if(mxmlGetNodeAttrsValue(eleChild) == true)
		return true;

	strName = eleChild->value.element.name;
	printf("%s:%s\n",strName,eleChild->child->value.text.string);
	while(1)
	{
		eleChild = mxmlWalkNext(eleChild, treeRoot, MXML_NO_DESCEND);
		if(eleChild == NULL || (eleChild->child == NULL && eleChild->value.element.attrs == NULL))
			return false;
		RecursionNodes(eleChild,root);

		if(mxmlGetNodeAttrsValue(eleChild) == true)
			return true;
		strName = eleChild->value.element.name;
		printf("%s:%s\n",strName,eleChild->child->value.text.string);
	}

	return true;
}


/* --------------------------------------------------------------------------*/
/** 
 * @Synopsis  
 * 
 * @Param p
 * 
 * @Returns true or false   
 */
/* ----------------------------------------------------------------------------*/
mxml_node_t* ParserDataXML(void* buf, int len)
{
	char *msg = (char *)buf;
	int num = len;
	char ch[] = "\n    ";
	char ch2[] = "\r\n\t\t";
	char ch3[] = "\r\n\t";
	char ch4[] = "\r\n";
	char ch5[] = ">\n";

	char *pbuffer = (char *)malloc(num);
	char *pEndPos = FilterCharacters(pbuffer, msg, num, ch);

	char *pSecBuffer = (char *)malloc(num);
	pEndPos = FilterCharacters(pSecBuffer, pbuffer, pEndPos - pbuffer, ch2);
	free(pbuffer);

	char *pThdBuffer = (char *)malloc(num);
	memset(pThdBuffer, 0, num);
	pEndPos = FilterCharacters(pThdBuffer, pSecBuffer, pEndPos - pSecBuffer, ch3);
	free(pSecBuffer);

	char *pFthBuffer = (char *)malloc(num);
	memset(pFthBuffer, 0, num);
	pEndPos = FilterCharacters(pFthBuffer, pThdBuffer, pEndPos - pThdBuffer, ch4);
	free(pThdBuffer);

	char *pFifthBuffer = (char *)malloc(num);
	memset(pFifthBuffer, 0, num);
	pEndPos = FilterCharacters(pFifthBuffer, pFthBuffer, pEndPos - pFthBuffer, ch5);
	free(pFthBuffer);

	char *pDstBuffer = (char *)malloc(num);
	memset(pDstBuffer, 0, num);
	IsNeedIconv(pFifthBuffer, pEndPos - pFifthBuffer, pDstBuffer, num);

	mxml_node_t *treeRoot, *ele;
	treeRoot = ele = NULL;
	treeRoot = mxmlLoadString(treeRoot, pDstBuffer, mxmlTypeCallback);
	return treeRoot;
	/*
	if(treeRoot == NULL)
	  return false;
	if(*(int *)pDstBuffer == *(int *)"<?xm")
		treeRoot->child = treeRoot->last_child;
	ele = treeRoot;
	if( RecursionNodes(ele,treeRoot) != true)
	  return false;

	free(pFifthBuffer);
	free(pDstBuffer);
	return true;
	*/
}


/* --------------------------------------------------------------------------*/
/** 
 * @Synopsis  
 * 
 * @Param sourcebuf
 * @Param sourcelen
 * @Param destbuf
 * @Param destlen
 * 
 * @Returns   
 */
/* ----------------------------------------------------------------------------*/
int IsNeedIconv(char *sourcebuf,size_t sourcelen,char *destbuf,size_t destlen)
{
	return Gb2312ToUtf8(sourcebuf, sourcelen, destbuf, destlen);
}

/* --------------------------------------------------------------------------*/
/** 
 * @Synopsis  
 * 
 * @Returns void* 
 */
/* ----------------------------------------------------------------------------*/
void* RecvDataXMLFromFile()
{
	FILE *fp = fopen("./config/test.xml","r");

	mxml_node_t *treeRoot,*ele;

	treeRoot = mxmlLoadFile(treeRoot, fp, mxmlTypeCallback);

	ele = treeRoot;

	RecursionNodes(ele, treeRoot);

	return NULL;
}


/* --------------------------------------------------------------------------*/
/** 
 * @Synopsis  found mxml's type
 * 
 * @Param node mxml_node_t* node
 * 
 * @Returns   mxml_type_t
 */
/* ----------------------------------------------------------------------------*/
mxml_type_t mxmlTypeCallback(mxml_node_t *node)
{
	const char *type;
	
	type = mxmlElementGetAttr(node, "type");

	if(type == NULL)
		type = node->value.element.name;
	if(!strcmp(type, "integer"))
		return (MXML_INTEGER);
	else if(!strcmp(type, "opaque"))
		return (MXML_OPAQUE);
	else if(!strcmp(type, "real"))
		return (MXML_REAL);
	else
		return (MXML_TEXT);
}


/* --------------------------------------------------------------------------*/
/** 
 * @Synopsis filter some useless characters
 * 
 * @Param void* dstBuff
 * @Param void* srcBuff
 * @Param int blen
 * @Param char* fStr
 * 
 * @Returns void* 
 */
/* ----------------------------------------------------------------------------*/
void* FilterCharacters(void* dstBuff, void* srcBuff, int blen, char* fStr)
{
	char *pHead = NULL, *pPos = NULL,
			*p = NULL, *pStr = fStr;
	pHead = srcBuff;
	pPos = dstBuff;
	int len = blen;
	
	while(1)
	{	
		if(NULL == (p = (char *)memmem(pHead, len, pStr, strlen(pStr))))
			break;
		if(strcmp(pStr, ">\n") == 0){
			memcpy(pPos, pHead, p - pHead + 1);
			pPos += p - pHead + 1;
			len = len - (p - pHead + strlen(pStr));
			pHead = p + strlen(pStr);
		}
		else{
			memcpy(pPos, pHead, p - pHead);
			pPos += p - pHead;
			len = len - (p - pHead + strlen(pStr));
			pHead = p + strlen(pStr);
		}
	}
	memcpy(pPos, pHead, blen - (pHead - (char *)srcBuff));	
	pPos += blen - (pHead - (char *)srcBuff);
	return pPos;
}
/* --------------------------------------------------------------------------*/
/** 
   * @Synopsis  
   * 
   * @Param treeRoot
   * @Param name
   * @Param treeRoot
   * @Param name
   * 
   * @Returns   
   */
/* ----------------------------------------------------------------------------*/
mxml_node_t* HaveNode(mxml_node_t* treeRoot, const char* name)
{
    mxml_node_t *node;
    node = mxmlFindElement(treeRoot, treeRoot, name, NULL, NULL, MXML_DESCEND);
    //for(; node != NULL; node = mxmlFindElement(treeRoot, treeRoot, name, NULL, NULL, MXML_DESCEND));
    return node;
}


mxml_node_t* SearchSubNode(mxml_node_t* subNode, mxml_node_t* root, const char* name)
{
    mxml_node_t *node = subNode, *treeRoot = root, *sNode = NULL;;
	
    node = mxmlFindElement(sNode, treeRoot, name, NULL, NULL, MXML_DESCEND);
    //for(; node != NULL; node = mxmlFindElement(treeRoot, treeRoot, name, NULL, NULL, MXML_DESCEND));
    return node;
}

/* --------------------------------------------------------------------------*/
/** 
  * @Synopsis  
  * 
  * @Param node
  * 
  * @Returns   
  */
/* ----------------------------------------------------------------------------*/
char* GetKeyValue(mxml_node_t* node, char *name)
{
	int i = 0;
    //case: <UpdatePort>38888</UpdatePort>
    if(strcmp(node->value.element.name, name) == 0)
	{
		if(NULL == node->child)
			return NULL;
	    return node->child->value.text.string;
	}
    //case: <Support NAT="true" Scheme="true" PtzLockRet="true" Video2="false" />
    int num = node->value.element.num_attrs;
    if(num < 0)
        return NULL;

    for(; i < num; i++){
        if(strcmp(node->value.element.attrs[i].name, name) == 0)
            return node->value.element.attrs[i].value;
    }
    return NULL;
}

/** 
 * @brief 
 * 
 * @param node
 * @param str
 * @param whitespace
 * 
 * @return 
 */
bool SetNodeTextValue(mxml_node_t* node, char* str, int whitespace)
{
	return mxmlSetText(node, whitespace, str);
}

/* --------------------------------------------------------------------------*/
/** 
 * @Synopsis  
 * 
 * @Param node
 * @Param name
 * @Param value
 * 
 * @Returns   
 */
/* ----------------------------------------------------------------------------*/
void SetNodeAttrValue(mxml_node_t* node, const char* name, const char* value)
{
	return mxmlElementSetAttr(node, name, value);
}
/** 
 * @brief 
 * 
 * @param node
 * @param str
 * 
 * @return 
 */
bool SetNodeElementValue(mxml_node_t* node, char* str)
{
	return mxmlSetElement(node, str);
}

/* --------------------------------------------------------------------------*/
/** 
 * @Synopsis  Test Module
 * 
 * @Param argc
 * @Param argv
 * 
 * @Returns   
 */
/* ----------------------------------------------------------------------------*/
#ifdef DEBUG_TEST_MODE
static FILE *fp;
int main(int argc, char *argv)
{
//	static FILE *fp;
	fp = fopen("./config/test.xml","wb");
	if(fp == NULL)
	{
		printf("fopen error\n");
		return;
	}
/*
	mxml_node_t *root;
	root = CreateXMLFile(fp);

	fclose(fp);
*/
//	RecvDataXMLFromFile();

	pthread_t recvId;

	pthread_create(&recvId, NULL, RecvDataXML, (void*)NULL);

	pthread_join(recvId, NULL);

	return 0;
}
#endif
